package com.slipper.common.util;

import com.google.gson.*;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.slipper.common.gson.GsonExclusionStrategy;

import java.io.IOException;
import java.lang.reflect.Type;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class GsonUtil {
    public static Gson getInstance() {
        return new GsonBuilder()
                .setDateFormat("yyyy-MM-dd HH:mm:ss")
                .setExclusionStrategies(new GsonExclusionStrategy())
                .registerTypeAdapter(String.class, new StringTypeAdapter())
                .registerTypeAdapter(java.util.Date.class, new UDateTypeAdapter())
                .registerTypeAdapter(java.sql.Date.class, new SDateTypeAdapter())
                .registerTypeAdapter(java.time.LocalDateTime.class, new LDateTypeAdapter())
                .serializeNulls()
                .create();
    }
    
    public static String toJson(Object object) {
        return getInstance().toJson(object);
    }
    
    public static <T> T fromJson(String json, Type type) {
        return getInstance().fromJson(json, type);
    }

    public static <T> T fromJson(String json, Type type, T defaultValue) {
        if (StringUtil.isBlank(json)) {
            return defaultValue;
        }

        return getInstance().fromJson(json, type);
    }
}

class StringTypeAdapter extends TypeAdapter<String> {
    @Override
    public String read(JsonReader reader) throws IOException {
        if (reader.peek() == JsonToken.NULL) {
            reader.nextNull();
            return null;
        } else {
            return reader.nextString();
        }
    }

    @Override
    public void write(JsonWriter writer, String value) throws IOException {
        if (value == null || value.trim().isEmpty()) {
            writer.nullValue();
        } else {
            writer.value(value);
        }
    }
}

class UDateTypeAdapter extends TypeAdapter<java.util.Date> {
    @Override
    public java.util.Date read(JsonReader reader) throws IOException {
        if (reader.peek() == JsonToken.NULL) {
            reader.nextNull();
            return null;
        } else {
            try {
                return DateUtil.parseDate(reader.nextString());
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public void write(JsonWriter writer, java.util.Date value) throws IOException {
        writer.value(value == null ? null : DateUtil.formatTime(value));
    }
}

class SDateTypeAdapter extends TypeAdapter<java.sql.Date> {
    @Override
    public java.sql.Date read(JsonReader reader) throws IOException {
        if (reader.peek() == JsonToken.NULL) {
            reader.nextNull();
            return null;
        } else {
            try {
                return new java.sql.Date(DateUtil.parseDate(reader.nextString()).getTime());
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public void write(JsonWriter writer, java.sql.Date value) throws IOException {
        if (value == null) {
            writer.nullValue();
        } else {
            writer.value(DateUtil.formatDate(value));
        }
    }
}


class LDateTypeAdapter extends TypeAdapter<java.time.LocalDateTime> {
    @Override
    public java.time.LocalDateTime read(JsonReader reader) throws IOException {
        if (reader.peek() == JsonToken.NULL) {
            reader.nextNull();
            return null;
        } else {
            return LocalDateTime.parse(reader.nextString(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        }
    }

    @Override
    public void write(JsonWriter writer, java.time.LocalDateTime value) throws IOException {
        if (value == null) {
            writer.nullValue();
        } else {
            writer.value(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }
    }
}